/**
 * * 自定义工具
 */
import crypto from 'crypto'
import md5 from 'blueimp-md5'
import uuid from 'uuid/v4'
import { apiMethods } from '_api'

/**
 * @description 日期格式化
 * @author fanzhongxu
 * @param {Date | String | Number} oldDate 待格式化的日期，可以是Date对象也可以是字符串
 * @param {string} [fmt='yyyy年MM月dd日'] 格式化后的格式
 * @returns 格式化后的字符串
 */
export function formatDate(oldDate, fmt = 'yyyy年MM月dd日') {
  let date = new Date()
  if (typeof oldDate === 'string' || typeof oldDate === 'number') {
    date = new Date(+oldDate)
  } else {
    date = oldDate
  }
  if (/(y+)/.test(fmt)) {
    fmt = fmt.replace(
      RegExp.$1,
      (date.getFullYear() + '').substr(4 - RegExp.$1.length)
    )
  }
  const o = {
    'M+': date.getMonth() + 1,
    'd+': date.getDate(),
    'h+': date.getHours(),
    'm+': date.getMinutes(),
    's+': date.getSeconds(),
  }
  function padLeftZero(str) {
    return ('00' + str).substr(str.length)
  }
  for (const k in o) {
    if (new RegExp(`(${k})`).test(fmt)) {
      const str = o[k] + ''
      fmt = fmt.replace(
        RegExp.$1,
        RegExp.$1.length === 1 ? str : padLeftZero(str)
      )
    }
  }
  return fmt
}

/**
 * @description 将objB的属性值赋值给objA的同名属性
 * @author fanzhongxu
 * @param {Object} objA 待赋值对象
 * @param {Object} objB 赋值对象
 */
export function listAssign(objA, objB) {
  Object.keys(objA).forEach(key => {
    objA[key] = objB[key] || objA[key]
  })
}

/**
 * @description 判断对象是否含有空属性(null | undefined)
 * @author fanzhongxu
 * @param {Object} obj 待检测对象
 * @returns {boolean} 如果对象不存在、对象为{}或者对象含有空属性返回true，否则返回false
 */
export function isObjectHasEmpty(obj) {
  if (!obj) return true
  const keys = Object.keys(obj)
  if (keys.length <= 0) return true
  for (const key of keys) {
    if (!obj[key]) return true
  }
  return false
}

/**
 * @description （对象/数组）深拷贝
 * @author fanzhongxu
 * @param {*} v 待拷贝对象/数组
 * @returns 拷贝后的对象/数组
 */
export function deepClone(v) {
  const o = v.constructor === Array ? [] : {}
  for (var i in v) {
    o[i] = typeof v[i] === 'object' ? deepClone(v[i]) : v[i]
  }
  return o
}

/**
 * @description 获得数据的类型
 * @author fanzhongxu
 * @param {*} para 待判断类型的参数
 * @returns 类型（小写）
 */
export function getType(para) {
  const type = typeof para
  if (type === 'number' && isNaN(para)) return 'NaN'
  if (type !== 'object') return type
  return Object.prototype.toString
    .call(para)
    .replace(/[\[\]]/g, '') // eslint-disable-line
    .split(' ')[1]
    .toLowerCase()
}

/**
 * @description 防抖（节流优化版）
 * @author fanzhongxu
 * @param {function} fn 函数
 * @param {*} delay 时间间隔阈值
 * @returns 处理过后的函数
 */
export function throttle(fn, delay) {
  let timer
  let last = 0
  return function() {
    const context = this
    const args = arguments
    const now = +new Date()

    if (now - last < delay) {
      clearTimeout(timer)
      timer = setTimeout(() => {
        last = now
        fn.apply(context, args)
      }, delay)
    } else {
      last = now
      fn.apply(context, args)
    }
  }
}

/**
 * * AES 加密解密方法
 */
export const aes = {
  /**
   * @description AES加密
   * @author fanzhongxu
   * @param {*} data 要加密的数据
   * @param {*} key 密钥
   * @returns 加密后的数据
   */
  aesEncrypt(data, key = 'vpxLno996k5G3Psr', iv = 'nLEDzjWjno996cY9') {
    let sign = ''
    const cipher = crypto.createCipheriv('aes-128-cbc', key, iv)
    sign += cipher.update(data, 'utf8', 'hex')
    sign += cipher.final('hex')
    return sign
  },

  /**
   * @description AES解密
   * @author fanzhongxu
   * @param {*} encrypted 要解密的加密数据
   * @param {*} key 密钥
   * @returns 解密后的数据
   */
  aesDecrypt(encrypted, key = 'vpxLno996k5G3Psr', iv = 'nLEDzjWjno996cY9') {
    let src = ''
    const cipher = crypto.createDecipheriv('aes-128-cbc', key, iv)
    src += cipher.update(encrypted, 'hex', 'utf8')
    src += cipher.final('utf8')
    return src
  },
}

/**
 * @description 慢hash
 * @author fanzhongxu
 * @param {*} obj 需要hash的内容
 * @returns md5后的数据
 */
export function slowHash(str) {
  let s = str
  for (let i = 0; i <= 10000; i++) {
    s = md5(s)
  }
  return s
}

/**
 * @description 生成uuid（Universally Unique Identifier）
 * @author fanzhongxu
 * @returns 返回uuid
 */
export function generateUUID() {
  return uuid()
}

/**
 * * 浮点数运算
 */
export const floatMath = {
  /**
   * @description 浮点数乘法
   * @author fanzhongxu
   * @param {*} arg1 被乘数
   * @param {*} arg2 乘数
   * @returns 积
   */
  mul(arg1, arg2) {
    var m = 0
    var s1 = arg1.toString()
    var s2 = arg2.toString()
    try {
      m += s1.split('.')[1].length
    } catch (e) {}
    try {
      m += s2.split('.')[1].length
    } catch (e) {}
    const intNumber = Number(s1.replace('.', '')) * Number(s2.replace('.', ''))
    const divNumber = Math.pow(10, m)
    return this.div(intNumber, divNumber)
  },

  /**
   * @description 浮点数加法
   * @author fanzhongxu
   * @param {*} arg1 参数1
   * @param {*} arg2 参数2
   * @returns 和
   */
  add(arg1, arg2) {
    var r1, r2, m
    try {
      r1 = arg1.toString().split('.')[1].length
    } catch (e) {
      r1 = 0
    }
    try {
      r2 = arg2.toString().split('.')[1].length
    } catch (e) {
      r2 = 0
    }
    m = Math.pow(10, Math.max(r1, r2))
    const intNumber = this.mul(Number(arg1), m) + this.mul(Number(arg2), m)
    return this.div(intNumber, m)
  },

  /**
   * @description 浮点数减法
   * @author fanzhongxu
   * @param {*} arg1 被减数
   * @param {*} arg2 减数
   * @returns 差
   */
  sub(arg1, arg2) {
    return this.add(arg1, -arg2)
  },

  /**
   * @description 浮点数除法
   * @author fanzhongxu
   * @param {*} arg1 被除数
   * @param {*} arg2 除数
   * @returns 商
   */
  div(arg1, arg2) {
    var t1 = 0
    var t2 = 0
    var r1
    var r2
    try {
      t1 = arg1.toString().split('.')[1].length
    } catch (e) {}
    try {
      t2 = arg2.toString().split('.')[1].length
    } catch (e) {}
    r1 = Number(arg1.toString().replace('.', ''))
    r2 = Number(arg2.toString().replace('.', ''))
    return (r1 / r2) * Math.pow(10, t2 - t1)
  },

  /**
   * @description 判断两个浮点数是否相等
   * @author fanzhongxu
   * @param {*} arg1 参数1
   * @param {*} arg2 参数2
   * @returns 比较结果
   */
  equaly(arg1, arg2) {
    return Math.abs(arg1 - arg2) < Math.pow(2, -53)
  },
}

/**
 * @description 映射 api 方法
 * @param {Array} apis 需要映射的api方法列表
 * @returns {方法名: 方法函数}
 */
export const mapApi = apis => {
  const result = apis.reduce((obj, cur) => {
    obj[cur] = apiMethods[cur]
    return obj
  }, {})
  return result
}
